package com.game.level;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.ws.Dispatch;

import org.jdom.Element;

import com.game.objects.Enemy;
import com.game.objects.Environment;
import com.game.system.Settings;
import com.game.system.Utils;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.effects.ProjectedTextureUtil;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

@SuppressWarnings("serial")
public class Terrain extends Node{

	private Element terrainInfo;
	private TerrainPage tb;
	private Environment ev;
	private boolean ready=false;
	private int heightMapSize=Settings.getConfig().getInt(Settings.TERRAIN_MAP_SIZE,64);
	private int texturSize=Settings.getConfig().getInt(Settings.TERRAIN_TEXTURE_SIZE,64);
	private ArrayList<Vector2f> deforms = new ArrayList<Vector2f>();
	private ArrayList<Spatial> models = new ArrayList<Spatial>();
	private ImageIcon tex;
	private Texture texTerrain;
	private Texture texDetail;
	
	public Terrain(Element terrainInfo) {
		this.terrainInfo=terrainInfo;
	}

	public void setUp(Environment ev){
		if(!ready){
			this.texDetail= ev.getDetailTexture();
			this.ev=ev;
			setUPTerrain(ev);
			setUpObjects(ev);
			setUpEnemys();
		}
		ready=true;
	}
	
	public boolean checkSimpleCollision(Spatial obj){
		Vector3f newPos2 = obj.getWorldTranslation().subtract(getWorldTranslation()).subtract(tb.getLocalTranslation());
		float height = tb.getHeight(newPos2)-25f;
		if(Float.isNaN(height)){
			return false;
		}
		if(obj.getWorldTranslation().getY() <= height){
			Vector3f tmp = newPos2.divide(tb.getStepScale());
			deforms.add(new Vector2f(tmp.getX(),tmp.getZ()));
			return true;
		}
			
		return false;
	}

	
	private void setUPTerrain(Environment ev){
		Element terrain = terrainInfo.getChild("terrain");
		URL grayScale=Environment.class.getClassLoader().getResource("com/data/texture/"+terrain.getAttributeValue("src"));
		ImageIcon grayMap = new ImageIcon(grayScale);

		Image in = grayMap.getImage();
		Image out;
		float multi=1;
		if(grayMap.getIconWidth()==heightMapSize){
			out = in;
		}else{
			out = new BufferedImage(heightMapSize, heightMapSize, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = ((BufferedImage)out).createGraphics();
			g.drawImage(in, 0, 0,heightMapSize,heightMapSize, null);
			multi = 128/heightMapSize;
		}
		
		ImageBasedHeightMap heightMap = new ImageBasedHeightMap(out);
		Vector3f terrainScale = new Vector3f(10f*multi,0.3f,5.4f*multi);
   
	    tb = new TerrainPage("Terrain", 33,heightMap.getSize()+1, terrainScale,
                heightMap.getHeightMap(),
                false);
	    
	    tb.setDetailTexture(1, 16);
	    tb.setLocalTranslation(new Vector3f(0,-25f,-128f*2.5f));
	    
	    ev.getEnvironment().setRenderState(ev.cs);

	    ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
	            heightMap);
	    pt.addTexture(ev.grass,-128, 0, 128);
	    pt.addTexture(ev.dirt, 0, 128, 255);
	    pt.addTexture(ev.highest, 128, 255,384);
	    pt.createTexture(texturSize);
	    tex = pt.getImageIcon();
        
        attachChild(tb);
        
        tb.setModelBound(new BoundingBox());
	    tb.updateModelBound();
	    tb.updateWorldBound();
	    tb.updateGeometricState(0f, false);
        
        setTextures();  
	}
	
	


	@SuppressWarnings("unchecked")
	private void setUpObjects(Environment ev){
		List<Element> objects = terrainInfo.getChildren("obj");
		ListIterator<Element> list = objects.listIterator();
		
		while(list.hasNext()){
			Element object = list.next();
			float x,y,z;
			x = Float.parseFloat(object.getAttributeValue("x"));
			z = Float.parseFloat(object.getAttributeValue("z"));
			String ys = object.getAttributeValue("y");

			Spatial model = Utils.loadModel(object.getAttributeValue("src"));
			model.setLocalTranslation(x, 0, z);
			model.setModelBound(new BoundingBox());
			model.updateModelBound();
			attachChild(model);
			models.add(model);
			
			if(ys.equals("auto")){
				updateModelY(model);
			}else{
				y = Float.parseFloat(ys);
				model.getLocalTranslation().setY(y-25f);
			}

		}
	}
	
	@SuppressWarnings("unchecked")
	private void setUpEnemys(){
		List<Element> enemys = terrainInfo.getChildren("enemy");
		ListIterator<Element> list = enemys.listIterator();
		while(list.hasNext()){
			Element info = list.next();
			Enemy e = new Enemy(info, this);
			ev.addEnemy(e);
		}
	}
	
	
	private void setTextures(){
		if(texTerrain==null){
			texTerrain = TextureManager.loadTexture(
		            tex.getImage(),
		            Texture.MM_LINEAR_LINEAR,
		            Texture.FM_LINEAR, true);
	        texTerrain.setStoreTexture(false);
	        texTerrain.setApply(Texture.AM_COMBINE);
	        texTerrain.setCombineFuncRGB(Texture.ACF_MODULATE);
	        texTerrain.setCombineSrc0RGB(Texture.ACS_TEXTURE);
	        texTerrain.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
	        texTerrain.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);
	        texTerrain.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
	        texTerrain.setCombineScaleRGB(1.0f);
		}else{
			texTerrain.setImage( TextureManager.loadImage(tex.getImage(), true));
		}
        TextureState ts = (TextureState) tb.getRenderState(RenderState.RS_TEXTURE);
        if(ts==null){
            ts = ev.display.getRenderer().createTextureState();
            ts.setEnabled(true);
            ts.setTexture(texTerrain, 0);  
            ts.setTexture(texDetail, 1);
        }else{
        	ts.delete(0);
        }
        tb.setRenderState(ts);	
        tb.updateRenderState();
	}
	public void updateModelY(Spatial model){
		float y = tb.getHeightFromWorld(model.getLocalTranslation())-25f;
		if(!Float.isNaN(y)){
			model.getLocalTranslation().setY(y);
		}else{
			System.err.println("Warning: "+model.getName() +" could not set auto y ("+model.getLocalTranslation()+")!");
		}
		
	}


	public void update(){
		if(!deforms.isEmpty()){
			float mult = (float)texturSize/(float)heightMapSize;
			Graphics2D g = (Graphics2D) tex.getImage().getGraphics();
			g.setColor(Color.black);
			ListIterator<Vector2f> list = deforms.listIterator();			
			while (list.hasNext()) {
				Vector2f vec = list.next();
				Vector2f n = new Vector2f(vec.x+heightMapSize/2,vec.y+heightMapSize/2);
				tb.addHeightMapValue((int)n.x,(int)n.y,-10);
				g.fillRect((int)(n.x*mult)-1, texturSize-(int)(n.y*mult)-1, 2, 2);
			}
			deforms.clear();
			g.dispose();
			ListIterator<Spatial> listM = models.listIterator();
			while(listM.hasNext()){
				updateModelY(listM.next());
			}
			setTextures();
			tb.updateFromHeightMap();
		}


	}
	
	
}
